mod handlers;
mod utils;

use crate::utils::jwt::validate_token;
use axum::extract::Request;
use axum::http::HeaderMap;
use axum::middleware::Next;
use axum::response::Html;
use axum::routing::{Route, delete, patch};
use axum::{
    Router,
    http::StatusCode,
    middleware,
    response::IntoResponse,
    routing::{get, get_service, post},
};
use http::{Method, header};
use std::io;
use tower_http::compression::CompressionLayer;
use tower_http::trace::TraceLayer;
use tower_http::{
    cors::{Any, CorsLayer},
    services::{ServeDir, ServeFile},
};
use tracing::{debug, info};

#[tokio::main]
async fn main() {
    // 加载环境变量
    let _ = dotenvy::dotenv();

    // 日志追踪器
    tracing_subscriber::fmt::init();
    info!("Starting server");

    // 数据库连接
    let database_url = std::env::var("SQLITE_DB_URL").expect("SQLITE_DB_URL not set");
    let pool = sqlx::SqlitePool::connect(&database_url)
        .await
        .expect("Error with pool connection");

    // 建表
    sqlx::query(
        r#"create table users
            (
                id       integer
                    primary key autoincrement,
                username text,
                password text,
                email    text
            );
            "#,
    )
    .execute(&pool)
    .await;
    sqlx::query(
        r#"create table posts
            (
                id         integer
                    primary key autoincrement,
                created_at datetime,
                updated_at datetime,
                deleted_at datetime,
                title      text,
                body       text
            );

            create index idx_posts_deleted_at
                on posts (deleted_at);
            "#,
    )
    .execute(&pool)
    .await;

    let postRouter = Router::new()
        .route("/posts", get(handlers::posts::get_posts))
        .route("/posts/:id", get(handlers::posts::get_one_post))
        .route("/posts", post(handlers::posts::create_post))
        .route("/posts/:id", patch(handlers::posts::update_post))
        .route("/posts/:id", delete(handlers::posts::delete_post))
        .route_layer(middleware::from_fn(auth));

    let userRouter = Router::new()
        .route("/users", post(handlers::uesr::register))
        .route("/auth/login", post(handlers::uesr::login));

    let userProfileRouter = Router::new()
        .route("/auth/profile", get(handlers::uesr::validateUser))
        .route_layer(middleware::from_fn(auth));

    // 跨域中间件
    let cors = CorsLayer::new().allow_origin(Any);

    let app = Router::new()
        // 合并router
        .merge(userRouter)
        .merge(postRouter)
        .merge(userProfileRouter)
        // 状态, 全路由可用的数据, 这里是数据连接池
        .with_state(pool)
        // 跨域
        .layer(cors)
        // 代码压缩层
        .layer(CompressionLayer::new())
        // http跟踪器
        .layer(TraceLayer::new_for_http());

    let listener = tokio::net::TcpListener::bind("127.0.0.1:4000")
        .await
        .unwrap();

    println!("listening on {}", listener.local_addr().unwrap());
    axum::serve(listener, app).await.unwrap();
}

#[derive(Clone)]
struct AuthHeader {
    id: String,
}

async fn auth(
    headers: HeaderMap,
    mut req: Request,
    next: Next,
) -> Result<impl IntoResponse, (StatusCode, String)> {
    // 提取 Authorization header
    let header = headers.get("Authorization").ok_or((
        StatusCode::UNAUTHORIZED,
        "missing authorization header".to_string(),
    ))?;

    let header_str = header
        .to_str()
        .map_err(|_| (StatusCode::BAD_REQUEST, "invalid authorization header"))
        .unwrap();

    let token = header_str.replace("Bearer ", "").trim().to_string();

    // 验证 token
    let claims = validate_token(token.as_str()).map_err(|e| {
        tracing::warn!("token validation failed: {:?}", e);
        (
            StatusCode::UNAUTHORIZED,
            "invalid or expired token".to_string(),
        )
    })?;

    // 将用户信息注入请求上下文
    req.extensions_mut().insert(AuthHeader { id: claims.sub });

    Ok(next.run(req).await)
}
